package com.atguigu.gulimall.search.thread;

import java.util.concurrent.*;

public class ThreadTest {
    public static class Thread1 extends Thread {
        @Override
        public void run() {
            System.out.println("extends Thread");
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果：" + i);
        }
    }

    public static class Runnable1 implements Runnable {
        @Override
        public void run() {
            System.out.println("implements Runnable");
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果：" + i);
        }
    }

    public static class Callable1 implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            System.out.println("implements Callable<Integer>");
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果：" + i);
            Thread.sleep(5_000);
            return i;
        }
    }

    /**
     * 1、Thread
     * 2、Runnable
     * 3、Callable
     * 4、线程池[ExecutorService]
     * <p>
     * 1、2不能获取返回值   3结合Future可以获取
     * 1、2、3不能资源控制  4可以控制
     *
     * @param args
     */
    public static void testThread(String[] args) throws ExecutionException, InterruptedException {
        //extends Thread
        Thread1 thread1 = new Thread1();
        thread1.start();
        Runnable1 runnable1 = new Runnable1();
        //implements Runnable
        Thread thread = new Thread(runnable1);
        thread.start();
        //implements Callable<Integer>
        Callable1 callable1 = new Callable1();
        /**
         * Future:可以获取异步的结果
         */
        FutureTask<Integer> futureTask = new FutureTask<>(callable1);
        Thread futureTaskThread = new Thread(futureTask);
        futureTaskThread.start();
        Integer result = futureTask.get();
        System.out.println(result);
        //业务中，以上三种都不使用【所有的多线程异步任务都交给线程池执行】


        System.out.println("线程池");
        //线程池 每个系统只有一两个线程池，每个异步都提交给池
        /**
         * 七大参数
         * int：corePoolSize：核心线程数，线程池创建好后就准备就绪的线程数量，一直存活等待接受异步任务直接执行（可以设置过期 allowCoreThreadTimeOut 去关闭）
         * int：maximumPoolSize：最大线程数量，控制资源总量
         * long：keepAliveTime：存活时间，当前线程数量大于核心数量，空闲线程等待接受新任务的最大时间
         * TimeUnit：unit：时间单位
         * BlockingQueue<Runnable>：workQueue：阻塞队列，如果任务很多（超过最大线程数），将多的任务放在队列中，等待空闲线程来取出执行
         * ThreadFactory：threadFactory：线程的创建工厂
         * RejectedExecutionHandler：handler：如果阻塞队列满了，按照指定的拒绝策略拒绝执行任务
         *
         * 工作顺序
         * 1）、线程池创建，准备好 corePoolSize 数量的核心线程，准备接受任务
         * 1.1、corePoolSize 满了，将进来的任务放到阻塞队列中，空闲的 corePoolSize 会到队列中获取任务执行
         * 1.2、阻塞队列满了，开启新线程消费队列（最大只能开到 maximumPoolSize 指定的数量）
         * 1.3、maximumPoolSize 线程和阻塞队列都满了，用 RejectedExecutionHandler 拒绝任务
         * 1.4、阻塞队列消费完了，线程池有很多空闲，线程池在 keepAliveTime 时间以后释放到 corePoolSize
         *
         * 题目：一个线程池 core：7 max：20 queue：50 100并发怎么分配
         * 7个立即执行，50个放入队列，13个创建线程执行。30个拒绝策略
         * 不抛弃要执行，CallerRunsPolicy 策略
         *
         */
        ExecutorService threadPoolExecutor = new ThreadPoolExecutor(5,
                200,
                10,
                TimeUnit.MINUTES,
                new LinkedBlockingQueue<>(10_000),//默认是Integer.MAX_VALUE 根据压力测试调整大小
                Executors.defaultThreadFactory(),//可以自定义

                //AbortPolicy:丢弃新进来的任务，抛 RejectedExecutionException 异常
                //DiscardPolicy:丢弃新进来的任务，不做任何处理
                //DiscardOldestPolicy:丢弃未执行的最老的
                //CallerRunsPolicy:新任务采用同步调用（run方法）
                new ThreadPoolExecutor.AbortPolicy());
        threadPoolExecutor.execute(new Runnable1());

        FutureTask<Integer> integerFutureTask = new FutureTask<>(new Callable1());
        threadPoolExecutor.submit(integerFutureTask);
        Integer integer = integerFutureTask.get();
        System.out.println(integer);
    }

    /**
     * 核心n,最大n 固定数量线程池
     */
    private static ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
    /**
     * 核心0，无最大 均可回收的线程池
     */
    private static ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    /**
     * 核心n，无最大 定时任务的线程池
     */
    private static ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);
    /**
     * 单线程的线程池
     */
    private static ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

    /**
     * 测试 CompletableFuture 类的返回结果
     */
    public static void testCompletableFutureReturn(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main......start......");

        //无返回结果
        CompletableFuture.runAsync(() -> {
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果：" + i);
        }, singleThreadExecutor);

        //有返回结果
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果：" + i);
            return i;
        }, singleThreadExecutor);

        //获取返回结果
        Integer value = future.get();
        System.out.println("value:" + value);

        System.out.println("main......end......");
        singleThreadExecutor.shutdown();
    }

    /**
     * 测试 CompletableFuture 类的方法回调
     */
    public static void testCompletableFutureHandle(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main......start......");

        //有返回结果
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果：" + i);
            return i;
        }, singleThreadExecutor)
                //方法成功的回调
                .whenCompleteAsync((result, throwable) -> {
                    //虽然能得到异常信息，但是没法修改返回数据
                    System.out.println("结果是{" + result + "}，异常是{" + throwable + "}");
                })
                //方法异常的回调
                .exceptionally(throwable -> {
                    //可以感知异常，同时返回默认值
                    System.out.println("出现异常：" + throwable.getMessage());
                    return 10;
                })
                //方法完成后的处理
                .handle((result, throwable) -> {
                    System.out.println("方法完成，结果{" + result + "}，异常是{" + throwable + "}");
                    if (result != null) {
                        return result * 2;
                    } else {
                        return 0;
                    }
                });

        //获取返回结果
        Integer value = future.get();
        System.out.println("value:" + value);

        System.out.println("main......end......");
        singleThreadExecutor.shutdown();
    }


    /**
     * 测试线程串行化
     */
    public static void testThreadSerial(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main......start......");

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果：" + i);
            return i;
        }, singleThreadExecutor);

        /**
         * then***():新任务使用上一任务的线程池
         * then***sync():新任务使用新的线程池或指定线程池
         */
        //thenRun 不能获取上一步的执行结果
        future.thenRun(() -> {
            System.out.println("thenRun 任务启动了......");
        });

        //thenAccept 能获取上一步的执行结果，但是无法操作返回值
        future.thenAccept((result) -> {
            System.out.println("thenAccept 任务启动了......" + result);
        });

        //thenApply 能获取上一步的执行结果，可以修改返回值
        CompletableFuture<Integer> resultFuture = future.thenApply((result) -> {
            System.out.println("thenApply 任务启动了......" + result);
            return result * 2;
        });
        Integer result = resultFuture.get();
        System.out.println("value:" + result);

        System.out.println("main......end......");
        singleThreadExecutor.shutdown();
    }

    /**
     * 测试两任务组合
     */
    public static void test2ThreadGroup(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main......start......");

        CompletableFuture<Object> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务一开始，线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            try {
                Thread.sleep(2_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务一，运行结束");
            System.out.println();
            return i;
        }, cachedThreadPool);
        CompletableFuture<Object> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println();
            System.out.println("任务二开始，线程：" + Thread.currentThread().getId());
            try {
                Thread.sleep(1_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务二，运行结束");
            System.out.println();
            return "Hello";
        }, cachedThreadPool);

        /**
         * 两个都完成 Both
         */
        //runAfterBoth 不能感知前两个的结果
        future1.runAfterBothAsync(future2, () -> {
            System.out.println("runAfterBothAsync 【不能感知前两个的结果】 任务开始，线程：" + Thread.currentThread().getId());
        }, cachedThreadPool);

        //thenAcceptBothAsync 可以感知前两个的结果，无法返回
        future1.thenAcceptBothAsync(future2, (f1, f2) -> {
            System.out.println("thenAcceptBothAsync 【可以感知前两个的结果，无法返回】 任务开始，线程：" + Thread.currentThread().getId());
            System.out.println("    之前的结果:" + f1 + f2);
        }, cachedThreadPool);

        //thenCombineAsync 可以感知前两个结果，可以返回
        CompletableFuture<String> bothResult = future1.thenCombineAsync(future2, (f1, f2) -> {
            System.out.println("thenCombineAsync 【可以感知前两个结果，可以返回】 任务开始，线程：" + Thread.currentThread().getId());
            return f1 + ":" + f2;
        }, cachedThreadPool);

        /**
         * 任一个完成 Either
         */
        //runAfterEitherAsync 不能感知前两个的结果
        future1.runAfterEitherAsync(future2, () -> {
            System.out.println("runAfterEitherAsync 【不能感知前两个的结果，无返回值】 任务开始，线程：" + Thread.currentThread().getId());
        }, cachedThreadPool);

        //thenAcceptBothAsync 可以感知前两个的结果，无法返回
        future1.acceptEitherAsync(future2, (result) -> {
            System.out.println("thenAcceptBothAsync 【可以感知前两个的结果，无法返回】 任务开始，线程：" + Thread.currentThread().getId());
            System.out.println("    之前的结果:" + result);
        }, singleThreadExecutor);

        //thenCombineAsync 可以感知前两个结果，可以返回
        CompletableFuture<Object> resultEither = future1.applyToEitherAsync(future2, (result) -> {
            System.out.println("thenCombineAsync 【可以感知前两个结果，可以返回】 任务开始，线程：" + Thread.currentThread().getId());
            return result + ":" + result;
        }, singleThreadExecutor);

        System.out.println("main......end......");
    }

    /**
     * 测试多任务组合 testManyThreadGroup
     */
    public static void testManyThreadGroup(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main......start......");

        CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("查询商品图片信息 线程:" + Thread.currentThread().getId());
            return "hello.jpg";
        });
        CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("查询商品属性 线程:" + Thread.currentThread().getId());
            return "黑色+256G";
        });
        CompletableFuture<String> futureBrand = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品品牌 线程:" + Thread.currentThread().getId());
            return "华为";
        });

        CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(futureImg, futureAttr, futureBrand);
        Object o = anyFuture.get();//等待任一完成
        System.out.println(o);

        CompletableFuture<Void> allFuture = CompletableFuture.allOf(futureImg, futureAttr, futureBrand);
        allFuture.get();//等待所有完成

        System.out.println("main......end......");
    }

}
